﻿using System;

namespace Wisdom.Core.Generators
{
    /// <summary>
    /// Represents the sequential identity generator.
    /// </summary>
    public class SequentialIdentityGenerator : IIdentityGenerator, ISequenceGenerator
    {
        #region Private Constants
        //private const int Year = 2010;
        //private const int Month = 11;
        //private const int Day = 23;
        //private const int Hour = 0;
        //private const int Minute = 0;
        //private const int Second = 0;
        //private const int Millisecond = 0;
        #endregion

        #region Private Fields
        //private UInt16 counter = 0;
        //private readonly object syncObj = new object();
        //private readonly DateTime startingPoint = new DateTime(Year, Month, Day, Hour, Minute, Second, Millisecond).ToUniversalTime();
        #endregion

        #region Private Methods
        private static Guid NewSequentialGuid()
        {
            byte[] uid = Guid.NewGuid().ToByteArray();
            byte[] binDate = BitConverter.GetBytes(DateTime.UtcNow.Ticks);

            var secuentialGuid = new byte[uid.Length];

            secuentialGuid[0] = uid[0];
            secuentialGuid[1] = uid[1];
            secuentialGuid[2] = uid[2];
            secuentialGuid[3] = uid[3];
            secuentialGuid[4] = uid[4];
            secuentialGuid[5] = uid[5];
            secuentialGuid[6] = uid[6];
            // set the first part of the 8th byte to '1100' so     
            // later we'll be able to validate it was generated by us   

            secuentialGuid[7] = (byte)(0xc0 | (0xf & uid[7]));

            // the last 8 bytes are sequential,    
            // it minimizes index fragmentation   
            // to a degree as long as there are not a large    
            // number of Secuential-Guids generated per millisecond  

            secuentialGuid[9] = binDate[0];
            secuentialGuid[8] = binDate[1];
            secuentialGuid[15] = binDate[2];
            secuentialGuid[14] = binDate[3];
            secuentialGuid[13] = binDate[4];
            secuentialGuid[12] = binDate[5];
            secuentialGuid[11] = binDate[6];
            secuentialGuid[10] = binDate[7];

            return new Guid(secuentialGuid);
        }
        #endregion

        #region IIdentityGenerator Members
        /// <summary>
        /// Generates the identity.
        /// </summary>
        /// <returns>The generated identity instance.</returns>
        public object Generate()
        {
            return NewSequentialGuid();
        }

        #endregion

        #region ISequence Members
        /// <summary>
        /// Gets the next value of the sequence.
        /// </summary>
        public object Next
        {
            get { return Generate(); }
        }

        #endregion
    }
}
